package com.example.shardingmultdatasource.aspect;

import com.example.shardingmultdatasource.config.ContextConst;
import com.example.shardingmultdatasource.config.DataSource;
import com.example.shardingmultdatasource.config.DataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * 切换数据源的切面
 */
@Component
@Aspect
@Order(1) //这是关键，要让该切面调用先于AbstractRoutingDataSource的determineCurrentLookupKey()
@Slf4j
public class DynamicDataSourceAspect {

    @Pointcut("execution(* com.example.shardingmultdatasource.service..*.*(..))")
    public void dataSourcePointcut(){

    }

    @Before("dataSourcePointcut()")
    public void before(JoinPoint point) {
        try {
            DataSource annotationOfClass = point.getTarget().getClass().getAnnotation(DataSource.class);
//            String methodName = point.getSignature().getName();
//            Class[] parameterTypes = ((MethodSignature) point.getSignature()).getParameterTypes();
//            Method method = point.getTarget().getClass().getMethod(methodName, parameterTypes);
//            DataSource methodAnnotation = method.getAnnotation(DataSource.class);
            if(annotationOfClass!=null) {
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = attributes.getRequest();
                String shopId = request.getHeader("shopId");
                String merchantId = request.getHeader("merchantId");
                String dataSourceType = ContextConst.DataSourceType.DB0.name();
                if (org.apache.commons.lang3.StringUtils.isNotBlank(shopId)) {
                    long hash = Math.abs(shopId.hashCode());
                    int shardKey = Integer.parseInt(hash % 2 + "");
                    if (shardKey == 0) {
                        dataSourceType = ContextConst.DataSourceType.DB0.name();
                    } else {
                        dataSourceType = ContextConst.DataSourceType.DB1.name();
                    }
                    log.info("datasource = {} hash = {} shardingkey = {}", dataSourceType, hash, shardKey);

                }
                DataSourceContextHolder.setDataSource(dataSourceType);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

   /* @Before("execution(* com.example.shardingmultdatasource.service..*.*(..))")
    public void before(JoinPoint point) {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();

            DataSource annotationOfClass = point.getTarget().getClass().getAnnotation(DataSource.class);
            String methodName = point.getSignature().getName();
            Class[] parameterTypes = ((MethodSignature) point.getSignature()).getParameterTypes();
            Method method = point.getTarget().getClass().getMethod(methodName, parameterTypes);
            DataSource methodAnnotation = method.getAnnotation(DataSource.class);
            methodAnnotation = methodAnnotation == null ? annotationOfClass : methodAnnotation;
            String param = methodAnnotation != null ? methodAnnotation.merchantId() : "";
            if(StringUtils.isNotBlank(param)) {
                String[] parameterNames = ((MethodSignature) point.getSignature()).getParameterNames();
                //3.通过你需要获取的参数名称的下标获取到对应的值
                int paramIndex = ArrayUtils.indexOf(parameterNames, param);
                Object[] args = point.getArgs();
                String value = (String) args[paramIndex];
                String dataSourceType = "ds0";
                if (org.apache.commons.lang3.StringUtils.isNotBlank(value)) {
                    long hash = Math.abs(value.hashCode());
                    int shardKey = Integer.parseInt(hash % 2 + "");
                    if (shardKey == 0) {
                        dataSourceType = ContextConst.DataSourceType.DS0.name();
                    } else {
                        dataSourceType = ContextConst.DataSourceType.DS1.name();
                    }
                    log.info("datasource = {} hash = {} shardingkey = {}", dataSourceType, hash, shardKey);

                }
                DataSourceContextHolder.setDataSource(dataSourceType);
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }*/

    @After("dataSourcePointcut()")
    public void after(JoinPoint point) {
        DataSourceContextHolder.clearDataSource();
    }
}